#include "net/module/select.h"
#include <cstring>

namespace net {
Select::Select()
    : max_fd_(-1) {
  FD_ZERO(&r_set_);
  FD_ZERO(&w_set_);
}

bool Select::Init() {
  return true;
}

void Select::Stop() {
  max_fd_ = -1;
  Poller::Stop();
}

bool Select::Add(Event* ev, int flag) {
  if (ev == NULL || flag < 0) return false;
  std::map<int, Event*>::iterator it = ev_map_.find(ev->fd);
  if (it != ev_map_.end()) {
    LOG(WARNING)<< "fd already exist, fd: " << ev->fd;
    return false;
  }

  if (flag & EV_READ) {
    LOG(INFO)<< "add fd: " << ev->fd;
    FD_SET(ev->fd, &r_set_);
  }
  if (flag & EV_WRITE) {
    FD_SET(ev->fd, &w_set_);
  }

  if (ev->fd > max_fd_) max_fd_ = ev->fd;

  return ev_map_.insert(std::make_pair(ev->fd, ev)).second;
}

void Select::Del(Event* ev, int flag) {
  if (ev == NULL || flag < 0) return;
  std::map<int, Event*>::iterator it = ev_map_.find(ev->fd);
  if (it == ev_map_.end()) return;

  if (flag & EV_READ) {
    FD_CLR(ev->fd, &r_set_);
  }
  if (flag & EV_WRITE) {
    FD_CLR(ev->fd, &w_set_);
  }

  if (ev->fd == max_fd_) max_fd_ = -1;
  ev_map_.erase(ev->fd);
}

bool Select::Mod(Event* ev, int flag) {
  if (ev == NULL || flag < 0) return false;
  Del(ev, flag);
  return Add(ev, flag);
}

void Select::AdjustMaxFd() {
  std::map<int, Event*>::iterator it = ev_map_.begin();
  if (it == ev_map_.end()) {
    max_fd_ = -1;
    return;
  }
  max_fd_ = it->second->fd;
  ++it;

  while (it != ev_map_.end()) {
    if (max_fd_ < it->second->fd) {
      max_fd_ = it->second->fd;
    }
    ++it;
  }
}

void Select::Loop() {
  exit_ = false;
  while (!exit_) {
    if (max_fd_ == -1) {
      AdjustMaxFd();
      if (max_fd_ == -1) {
        ::sleep(1);
        continue;
      }
    }

    LOG(INFO)<< "max fd: " << max_fd_;

    fd_set rset, wset;
    ::memcpy(&rset, &r_set_, sizeof(fd_set));
    ::memcpy(&wset, &w_set_, sizeof(fd_set));
    struct timeval tv = { 0 };
    tv.tv_sec = 1;
    tv.tv_usec = 0;

    int trigger_cnt = ::select(max_fd_ + 1, &rset, &wset, NULL, &tv);
    if (trigger_cnt == -1) {
      if (errno != EINTR) {
        PLOG(WARNING)<< "select error";
      }
      continue;
    }

    std::map<int, Event*>::iterator it = ev_map_.begin();
    for (int i = 0; i < trigger_cnt && it != ev_map_.end(); ++it) {
      Event* ev = it->second;
      short flag = 0;
      if (FD_ISSET(ev->fd, &rset)) {
        flag |= EV_READ;
      }
      if (FD_ISSET(ev->fd, &wset)) {
        flag |= EV_WRITE;
      }

      if (flag) {
        ev->cb(ev->fd, ev->arg, flag);
        ++i;
      }
    }
  }

  stop_cond_.Signal();
}

}
